#include "UpdateFunctions.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QElapsedTimer>
#include <QFileDialog>
#include <QScrollBar>

UpdateFunctions::UpdateFunctions(QObject *parent) : QObject(parent), m_logOutput(nullptr) {}

void UpdateFunctions::setLogOutput(QTextEdit *logOutput)
{
    m_logOutput = logOutput;
}

void UpdateFunctions::log(const QString &message)
{
    if (m_logOutput)
    {
        m_logOutput->append(message);
        m_logOutput->ensureCursorVisible(); // 确保光标可见
    }
    qDebug() << message;
    QCoreApplication::processEvents(); // 处理UI事件
}

void UpdateFunctions::clearAndScanFolder(const QString &folderPath, const QString &knownFacesDir,
                                         ImageChatService &imageChatService, Embedding &embedding,
                                         AIServiceSettings &aiSettings)
{
    log("开始扫描...");

    log("正在加载文件信息缓存...");

    FileManager::getInstance().loadFromJson("file_info.json");
    log("文件信息缓存已加载");

    log("开始扫描文件夹: " + folderPath);
    if (!folderPath.isEmpty())
    {
        FileManager::getInstance().scanDirectory(folderPath);
        log("扫描完成，更新后的件总数: " +
            QString::number(FileManager::getInstance().getFiles().size()));
        log("文件夹遍历完成，缓存已清空并重新填充");
    }

    log("开始运行人脸识别... 请耐心等待...");
    QString outputJsonPath = "face_recognition_results.json";
    recognizeFaces(false, knownFacesDir, folderPath, outputJsonPath);
    log("人脸识别完成");

    log("开始更新文件信息... 请耐心等待...");
    updateFaceInfo();
    log("文件人脸信息更新完成");

    log("正在更新文件内容类型... 请耐心等待...");
    updateContentType(imageChatService, aiSettings);
    log("文件内容类型更新完成");

    log("正在更新文件内容详细描述... 请耐心等待...");
    updateContentDescription(imageChatService, aiSettings);
    log("文件内容详细描述更新完成");

    log("正在更新文件内容嵌入向量... 请耐心等待...");
    updateEmbedding(embedding, aiSettings);
    log("文件内容嵌入向量更新完成");

    log("正在保存文件信息缓存...");
    FileManager::getInstance().saveToJson("file_info.json");
    log("文件信息缓存已保存");

    log("扫描完成");
}

void UpdateFunctions::appendScanFolder(const QString &folderPath, const QString &knownFacesDir,
                                       ImageChatService &imageChatService, Embedding &embedding,
                                       AIServiceSettings &aiSettings)
{
    log("开始扫描...");

    log("正在加载文件信息缓存...");
    FileManager::getInstance().loadFromJson("file_info.json");
    log("文件信息缓存已加载");

    if (!folderPath.isEmpty())
    {
        log("开始追加遍历文件夹: " + folderPath);

        QList<FileInfo> oldFiles = FileManager::getInstance().getFiles();
        log("当前文件数量: " + QString::number(oldFiles.size()));

        // 保存旧文件信息
        QMap<QString, FileInfo> oldFileMap;
        for (const FileInfo &file : oldFiles)
        {
            oldFileMap[file.filePath] = file;
        }

        FileManager::getInstance().scanDirectory(folderPath);
        log("扫描完成，更新后的文件总数: " +
            QString::number(FileManager::getInstance().getFiles().size()));

        // 更新旧文件信息到FileManager的m_files中
        QList<FileInfo> &updatedFiles = FileManager::getInstance().getFiles();
        for (FileInfo &file : updatedFiles)
        {
            if (oldFileMap.contains(file.filePath))
            {
                FileInfo oldInfo        = oldFileMap[file.filePath];
                file.peopleNames        = oldInfo.peopleNames;
                file.contentType        = oldInfo.contentType;
                file.contentDescription = oldInfo.contentDescription;
                file.contentEmbedding   = oldInfo.contentEmbedding;
            }
        }

        QList<FileInfo> newFiles = FileManager::getInstance().getNewFiles(oldFiles);
        log("新增文件数量: " + QString::number(newFiles.size()));

        if (newFiles.isEmpty())
        {
            log("没有新增文件，操作结束");
            return;
        }

        // 如果新增的图片数量大于10，则运行全部人脸识别
        if (newFiles.size() > 10)
        {
            log("开始运行人脸识别... 请耐心等待...");
            // 运行人脸识别
            QString outputJsonPath = "face_recognition_results.json";
            recognizeFaces(false, knownFacesDir, folderPath, outputJsonPath);
            log("人脸识别完成");

            log("开始更新文件信息... 请耐心等待...");
            updateFaceInfo();
            log("文件人脸信息更新完成");
        }
        else
        {
            // 对新增的图片逐个运行人脸识别
            for (const FileInfo &file : newFiles)
            {
                if (file.fileType == FileType::Image)
                {
                    log("开始运行人脸识别... " + file.fileName);
                    // 运行人脸识别
                    QString outputJsonPath = "face_recognition_results.json";
                    recognizeFaces(false, knownFacesDir, file.filePath, outputJsonPath);
                    FileManager::getInstance().updatePeopleNamesFromFaceRecognition(
                        "face_recognition_results.json");
                    log("人脸识别完成并更新了文件信息 " + file.fileName);
                }
            }
        }

        int processedCount = 0;
        for (const FileInfo &file : newFiles)
        {
            if (file.fileType == FileType::Image)
            {
                log("处理新文件: " + file.fileName);

                // 3. 更新contentType
                log("正在更新文件内容类型...");
                QString contentType = imageChatService.chatWithImage(
                    file.filePath, CONTENT_TYPE_PROMPT, aiSettings.getChatAIUrl(),
                    aiSettings.getChatAIKey(), aiSettings.getChatAIModel());
                contentType = parseContentType(contentType);
                log("文件内容类型更新为: " + contentType);

                // 4. 更新contentDescription
                log("正在更新文件内容详细描述...");
                QString contentDescription = imageChatService.chatWithImage(
                    file.filePath, CONTENT_DESCRIPTION_PROMPT, aiSettings.getChatAIUrl(),
                    aiSettings.getChatAIKey(), aiSettings.getChatAIModel());
                log("文件内容详细描述更新完成");

                // 5. 更新contentEmbedding
                log("正在更新文件内容嵌入向量...");
                QString sentence = QString("包含的人名：%1，文件类型：%2，文件详细描述：%3")
                                       .arg(file.peopleNames.join(", "))
                                       .arg(contentType)
                                       .arg(contentDescription);
                QVector<float> contentEmbedding = embedding.getEmbedding(
                    sentence, aiSettings.getEmbeddingAIUrl(), aiSettings.getEmbeddingAIKey(),
                    aiSettings.getEmbeddingAIModel());
                log("文件内容嵌入向量更新完成，维度: " + QString::number(contentEmbedding.size()));

                // 6. 更新文件信息
                FileManager::getInstance().updateFileInfo(file.filePath, contentType,
                                                          contentDescription, contentEmbedding);
                log("文件信息已更新");

                processedCount++;
                log("已处理 " + QString::number(processedCount) + " 个新文件，共 " +
                    QString::number(newFiles.size()) + " 个");
            }
        }

        log("追加文件夹遍历完成，共处理 " + QString::number(processedCount) + " 个新文件");
    }
    else
    {
        log("未选择文件夹，操作取消");
    }

    FileManager::getInstance().saveToJson("file_info.json");
    log("文件信息存已保存");

    log("扫描完成");
}

void UpdateFunctions::recognizeFaces(bool symbol, const QString &knownFacesDir,
                                     const QString &checkImagesDir, const QString &outputJsonPath,
                                     int cpus, double tolerance, bool showDistance)
{
    // 设置输出控件
    FaceRecognition::getInstance().setOutputWidget(m_logOutput);

    QList<FileInfo> &files = FileManager::getInstance().getFiles();
    int totalFiles         = files.size();

    // 添加时间统计
    QElapsedTimer totalTimer;
    totalTimer.start();

    log(QString("开始人脸识别，共 %1 个文件").arg(totalFiles));

    FaceRecognition::getInstance().runFaceRecognition(symbol, knownFacesDir, checkImagesDir,
                                                      outputJsonPath, cpus, tolerance,
                                                      showDistance);

    // 记录实际耗时
    qint64 totalElapsed = totalTimer.elapsed() / 1000; // 转换为秒

    log(QString("人脸识别完成: 实际耗时: %1").arg(formatRemainingTime(totalElapsed)));
}

void UpdateFunctions::updateFaceInfo()
{
    FileManager::getInstance().updatePeopleNamesFromFaceRecognition(
        "face_recognition_results.json");
}

void UpdateFunctions::updateContentType(ImageChatService &imageChatService,
                                        AIServiceSettings &aiSettings)
{
    QList<FileInfo> &files = FileManager::getInstance().getFiles();
    int totalFiles         = files.size();
    int processedFiles     = 0;

    // 添加时间统计
    QElapsedTimer totalTimer;
    totalTimer.start();
    QElapsedTimer fileTimer;

    log("开始更新文件内容类型...");
    QVector<qint64> processingTimes; // 存储每个文件的处理时间

    for (FileInfo &file : files)
    {
        if (file.fileType == FileType::Image)
        {
            fileTimer.restart();
            processedFiles++;

            log(QString("正在处理文件 (%1/%2): %3")
                    .arg(processedFiles)
                    .arg(totalFiles)
                    .arg(file.fileName));

            QString contentType = imageChatService.chatWithImage(
                file.filePath, CONTENT_TYPE_PROMPT, aiSettings.getChatAIUrl(),
                aiSettings.getChatAIKey(), aiSettings.getChatAIModel());

            file.contentType = parseContentType(contentType);

            // 计算并记录本次处理时间
            qint64 elapsed = fileTimer.elapsed();
            processingTimes.append(elapsed);

            // 计算平均处理时间
            qint64 avgTime = 0;
            for (qint64 time : processingTimes)
            {
                avgTime += time;
            }
            avgTime /= processingTimes.size();

            // 计算预计剩余时间
            int remainingFiles               = totalFiles - processedFiles;
            qint64 estimatedRemainingSeconds = (avgTime * remainingFiles) / 1000; // 转换为秒

            // 格式化输出时间信息
            QString timeInfo = QString("处理耗时: %1秒 | 平均每文件: %2秒 | 预计剩余: %3")
                                   .arg(elapsed / 1000.0, 0, 'f', 1)
                                   .arg(avgTime / 1000.0, 0, 'f', 1)
                                   .arg(formatRemainingTime(estimatedRemainingSeconds));

            log(QString("文件 %1 的内容类型更新为: %2 | %3")
                    .arg(file.fileName)
                    .arg(file.contentType)
                    .arg(timeInfo));
        }
    }

    // 输出总体统计信息
    qint64 totalElapsed = totalTimer.elapsed();
    log(QString("文件内容类型更新完成，共处理 %1 个文件，总耗时: %2")
            .arg(processedFiles)
            .arg(formatRemainingTime(totalElapsed / 1000)));
}

void UpdateFunctions::updateContentDescription(ImageChatService &imageChatService,
                                               AIServiceSettings &aiSettings)
{
    QList<FileInfo> &files = FileManager::getInstance().getFiles();
    int totalFiles         = files.size();
    int processedFiles     = 0;

    // 添加时间统计
    QElapsedTimer totalTimer;
    totalTimer.start();
    QElapsedTimer fileTimer;

    log("开始更新文件内容详细描述...");
    QVector<qint64> processingTimes; // 存储每个文件的处理时间

    for (FileInfo &file : files)
    {
        if (file.fileType == FileType::Image)
        {
            fileTimer.restart();
            processedFiles++;

            log(QString("正在处理文件 (%1/%2): %3")
                    .arg(processedFiles)
                    .arg(totalFiles)
                    .arg(file.fileName));

            file.contentDescription = imageChatService.chatWithImage(
                file.filePath, CONTENT_DESCRIPTION_PROMPT, aiSettings.getChatAIUrl(),
                aiSettings.getChatAIKey(), aiSettings.getChatAIModel());

            // 计算并记录本次处理时间
            qint64 elapsed = fileTimer.elapsed();
            processingTimes.append(elapsed);

            // 计算平均处理时间
            qint64 avgTime = 0;
            for (qint64 time : processingTimes)
            {
                avgTime += time;
            }
            avgTime /= processingTimes.size();

            // 计算预计剩余时间
            int remainingFiles               = totalFiles - processedFiles;
            qint64 estimatedRemainingSeconds = (avgTime * remainingFiles) / 1000; // 转换为秒

            // 格式化输出时间信息
            QString timeInfo = QString("处理耗时: %1秒 | 平均每文件: %2秒 | 预计剩余: %3")
                                   .arg(elapsed / 1000.0, 0, 'f', 1)
                                   .arg(avgTime / 1000.0, 0, 'f', 1)
                                   .arg(formatRemainingTime(estimatedRemainingSeconds));

            log(QString("文件 %1 的内容描述已更新 | %2").arg(file.fileName).arg(timeInfo));
        }
    }

    // 输出总体统计信息
    qint64 totalElapsed = totalTimer.elapsed();
    log(QString("文件内容详细描述更新完成，共处理 %1 个文件，总耗时: %2")
            .arg(processedFiles)
            .arg(formatRemainingTime(totalElapsed / 1000)));
}

void UpdateFunctions::updateEmbedding(Embedding &embedding, AIServiceSettings &aiSettings)
{
    QList<FileInfo> &files = FileManager::getInstance().getFiles();
    int totalFiles         = files.size();
    int processedFiles     = 0;

    // 添加时间统计
    QElapsedTimer totalTimer;
    totalTimer.start();
    QElapsedTimer fileTimer;

    log("开始更新文件内容嵌入向量...");
    QVector<qint64> processingTimes; // 存储每个文件的处理时间

    for (FileInfo &file : files)
    {
        fileTimer.restart();
        processedFiles++;

        log(QString("正在处理文件 (%1/%2): %3")
                .arg(processedFiles)
                .arg(totalFiles)
                .arg(file.fileName));

        QString sentence = QString("包含的人名：%1，文件类型：%2，文件详细描述：%3")
                               .arg(file.peopleNames.join(", "))
                               .arg(file.contentType)
                               .arg(file.contentDescription);

        file.contentEmbedding = embedding.getEmbedding(sentence, aiSettings.getEmbeddingAIUrl(),
                                                       aiSettings.getEmbeddingAIKey(),
                                                       aiSettings.getEmbeddingAIModel());

        // 计算并记录本次处理时间
        qint64 elapsed = fileTimer.elapsed();
        processingTimes.append(elapsed);

        // 计算平均处理时间
        qint64 avgTime = 0;
        for (qint64 time : processingTimes)
        {
            avgTime += time;
        }
        avgTime /= processingTimes.size();

        // 计算预计剩余时间
        int remainingFiles               = totalFiles - processedFiles;
        qint64 estimatedRemainingSeconds = (avgTime * remainingFiles) / 1000; // 转换为秒

        // 格式化输出时间信息
        QString timeInfo = QString("处理耗时: %1秒 | 平均每文件: %2秒 | 预计剩余: %3")
                               .arg(elapsed / 1000.0, 0, 'f', 1)
                               .arg(avgTime / 1000.0, 0, 'f', 1)
                               .arg(formatRemainingTime(estimatedRemainingSeconds));

        log(QString("文件 %1 的嵌入向量已更新，维度: %2 | %3")
                .arg(file.fileName)
                .arg(file.contentEmbedding.size())
                .arg(timeInfo));
    }

    // 输出总体统计信息
    qint64 totalElapsed = totalTimer.elapsed();
    log(QString("文件内容嵌入向量更新完成，共处理 %1 个文件，总耗时: %2")
            .arg(processedFiles)
            .arg(formatRemainingTime(totalElapsed / 1000)));
}

QString UpdateFunctions::parseContentType(const QString &contentType)
{
    QStringList validTypes = {"人物", "文本", "合照", "自拍",     "风景", "植物",
                              "食物", "建筑", "动物", "电子产品", "证件", "广告",
                              "截图", "网页", "玩具", "家具",     "其他"};
    QStringList types      = contentType.split(" ", Qt::SkipEmptyParts);
    QStringList validatedTypes;

    for (const QString &type : types)
    {
        QString trimmedType = type.trimmed();
        if (validTypes.contains(trimmedType))
        {
            validatedTypes.append(trimmedType);
        }
    }

    if (validatedTypes.isEmpty())
    {
        return "其他";
    }
    else
    {
        return validatedTypes.join(" ");
    }
}

QVector<float> UpdateFunctions::processImageForSearch(const QString &imagePath,
                                                      const QString &knownFacesDir,
                                                      ImageChatService &imageChatService,
                                                      Embedding &embedding,
                                                      AIServiceSettings &aiSettings)
{
    log("开始处理搜索图片: " + imagePath);

    // 1. 人脸识别
    log("开始运行人脸识别...");
    QString outputJsonPath = "face_recognition_results.json";
    recognizeFaces(false, knownFacesDir, imagePath, outputJsonPath);
    QStringList peopleNames = FileManager::getInstance().getPeopleNamesFromFaceRecognition(
        "face_recognition_results.json", imagePath);
    log("人脸识别完成，识别到的人名: " + peopleNames.join(", "));

    // 2. 更新contentType
    log("正在获取文件内容类型...");
    QString contentType =
        imageChatService.chatWithImage(imagePath, CONTENT_TYPE_PROMPT, aiSettings.getChatAIUrl(),
                                       aiSettings.getChatAIKey(), aiSettings.getChatAIModel());
    contentType = parseContentType(contentType);
    log("文件内容类型: " + contentType);

    // 3. 更新contentDescription
    log("正在获取文件内容详细描述...");
    QString contentDescription = imageChatService.chatWithImage(
        imagePath, CONTENT_DESCRIPTION_PROMPT, aiSettings.getChatAIUrl(), aiSettings.getChatAIKey(),
        aiSettings.getChatAIModel());
    log("文件内容详细描述获取完成");

    log("文件内容详细描述: " + contentDescription);

    // 4. 生成contentEmbedding
    log("正在生成文件内容嵌入向量...");
    QString sentence = QString("包含的人名：%1，文件类型：%2，文件详细描述：%3")
                           .arg(peopleNames.join(", "))
                           .arg(contentType)
                           .arg(contentDescription);
    QVector<float> contentEmbedding =
        embedding.getEmbedding(sentence, aiSettings.getEmbeddingAIUrl(),
                               aiSettings.getEmbeddingAIKey(), aiSettings.getEmbeddingAIModel());
    log("文件内容嵌入向量生成完成，维度: " + QString::number(contentEmbedding.size()));

    return contentEmbedding;
}

void UpdateFunctions::rescanFolder(const QString &folderPath, const QString &knownFacesDir,
                                   ImageChatService &imageChatService, Embedding &embedding,
                                   AIServiceSettings &aiSettings)
{
    log("开始重新扫描文件夹...");

    log("正在加载文件信息缓存...");
    FileManager::getInstance().loadFromJson("file_info.json");
    log("文件信息缓存已加载");

    if (!folderPath.isEmpty())
    {
        log("开始追加遍历文件夹: " + folderPath);

        QList<FileInfo> oldFiles = FileManager::getInstance().getFiles();
        log("当前文件数量: " + QString::number(oldFiles.size()));

        // 保存旧文件信息
        QMap<QString, FileInfo> oldFileMap;
        for (const FileInfo &file : oldFiles)
        {
            oldFileMap[file.filePath] = file;
        }

        FileManager::getInstance().scanDirectory(folderPath);
        log("扫描完成，更新后的文件总数: " +
            QString::number(FileManager::getInstance().getFiles().size()));

        // 更新旧文件信息到FileManager的m_files中
        QList<FileInfo> &updatedFiles = FileManager::getInstance().getFiles();
        for (FileInfo &file : updatedFiles)
        {
            if (oldFileMap.contains(file.filePath))
            {
                FileInfo oldInfo        = oldFileMap[file.filePath];
                file.peopleNames        = oldInfo.peopleNames;
                file.contentType        = oldInfo.contentType;
                file.contentDescription = oldInfo.contentDescription;
                file.contentEmbedding   = oldInfo.contentEmbedding;
            }
        }

        QList<FileInfo> newFiles = FileManager::getInstance().getNewFiles(oldFiles);
        log("新增文件数量: " + QString::number(newFiles.size()));
    }
    else
    {
        log("未选择文件夹，操作取消");
    }

    FileManager::getInstance().saveToJson("file_info.json");
    log("文件信息缓存已保存");

    log("扫描完成");
}

QString UpdateFunctions::formatRemainingTime(qint64 seconds)
{
    if (seconds < 60)
    {
        return QString("%1秒").arg(seconds);
    }
    else if (seconds < 3600)
    {
        int minutes = seconds / 60;
        int secs    = seconds % 60;
        return QString("%1分%2秒").arg(minutes).arg(secs);
    }
    else
    {
        int hours   = seconds / 3600;
        int minutes = (seconds % 3600) / 60;
        int secs    = seconds % 60;
        return QString("%1小时%2分%3秒").arg(hours).arg(minutes).arg(secs);
    }
}
